#include "Character.h"
#include "CharacterOwnedStates.h"
#include "Entity Manager.h"
#include "Steering Behavior.h"
#include "GLPrimitives.h"
#include "Collision.h"
#include "Transformations.h"
#include "Goal_Obey.h"
#include "Defines.hpp"
#include "PathPlanner.h"


// Used in remove function to remove collisions that have been resolved.
bool Resolved(Collision* aCollision)
{
	return aCollision->resolved;
}

//--------------------Constructor / Destructor ------------------------

Character::Character(string name, Vector2D pos, int type) : MovingEntity(name, pos, type)
{
	m_pStateMachine = new StateMachine<Character>(this);
	m_pStateMachine ->SetCurrentState(Idle::Instance());
	m_pObey = new Goal_Obey(this);


	selected = false;
	m_iGroupID = -1;
}


Character::~Character()
{
	NullDelete(m_pStateMachine);
}

//------------------Update---------------------------
// Update function

void Character::Update(double time_elapsed)
{

	MovingEntity::Update(time_elapsed);
	m_pStateMachine->Update();
	m_pObey->Process();
	HandleCollisions();

	return;
}


// -------------------- Render -------------------------------
void Character::Render()
{

	BaseGameEntity::Render();
	m_pObey->RenderAtPos(Pos());
	m_pSteeringBehavior->DrawPath();
	return;
}


// ------------------HandleMessage----------------------
// Handles the message...

bool Character::HandleMessage(const Message& msg)
{
	m_pStateMachine->HandleMessage(msg);
	m_pObey->HandleMessage(msg);
	switch(msg.Type)
	{
	case MoveToLocation :
		{
			int idx = m_pPlanner->GetClosestNodeToPosition(*(Vector2D*)msg.ExtraInfo);

			if(WorldMap->GetGraph()->GetNode(idx).NotOverLand())
			{
//				Vector2D ToPosition = WorldMap->GetGraph()->GetNode(idx).Pos() - Pos();
//				idx = m_pPlanner->GetClosestLandNode(ToPosition, idx);
				return false;
			}

			Vector2D pos = *(Vector2D*)msg.ExtraInfo;

			// Now begin to move to the location.
			Path* ToPos = new Path(Pos(), pos);

			m_pSteeringBehavior->SetPath(ToPos);
			m_pSteeringBehavior->FollowPathOn();

			return true;
			break;
		}
	case PathReady:
		{
			// Turn off arrive
			m_pSteeringBehavior->ArriveOff();

			Path* CalculatedPath = new Path(m_pPlanner->GetPath());
			m_pSteeringBehavior->SetPath(CalculatedPath);
			m_pSteeringBehavior->FollowPathOn();
			
			return true;
			break;
		}
	}
	return false;
}



Path* Character::GetPath()
{
	return m_pSteeringBehavior->GetPath();
}
// ==================================== Collisions =========================================

// ---------------------------HandleCollisions-----------------------------
// Adds all the new found collisions to the collision list of collisions, 
// updates current collisons and removes from the list any collisions that 
// have been resolved.

void Character::HandleCollisions()
{
	list<BaseGameEntity*> aList = EntityMgr->GetCollisions(this);
	list<BaseGameEntity*>::iterator iter = aList.begin();


	// Takes information from the colliding entity and decides which case
	// the current collision is. 
	while(iter != aList.end())
	{
		// Collision is with a building
		if((*iter)->EntityType() >= 200 && (*iter)->EntityType() < 300)
		{
			AddCollision(new Collision(Case4, this, (Building*)(*iter)));
		}

		// Else, collision is with a character

		else
		{
			if((*iter)->ID() == this->ID())
			{
				iter++;
				continue;
			}
			if(IsMoving() && !(*iter)->IsMoving())
				AddCollision(new Collision(Case2, this, (Character*)(*iter)));
			if(!IsMoving() && (*iter)->IsMoving())
				AddCollision(new Collision(Case1, this, (Character*)(*iter)));
		}

		iter++;
	}

	list<Collision*>::iterator colIter = m_Collisions.begin();

	for(colIter; colIter != m_Collisions.end(); colIter++)
	{
		(*colIter)->m_CollisionState.Update();
	} 
	
	m_Collisions.remove_if(Resolved);
	return;
}


// --------------AddCollision-------------------
// Adds a collision to the collision list for 
// later processing

void Character::AddCollision(Collision* newCollision)
{
	bool alreadyHere = false;
	list<Collision*>::iterator iter = m_Collisions.begin();
	for(iter; iter != m_Collisions.end(); iter++)
	{
		if(*(*iter) == *newCollision)											// Same Collision Case
			alreadyHere = true;
	}

	// Don't add it if it's already here.
	if(!alreadyHere)
	{
		m_Collisions.push_back(newCollision);
	}
	else
	{
		NullDelete(newCollision);
	}

	return;
}

// ---------------------------EvadeCharacter--------------------------
// Turns on the evade behavior for the given character. This is used
// in collision handling to make one character get out of another's
// path.

void Character::EvadeCharacter(Character* aChar)
{
	m_pSteeringBehavior->EvadeOn(aChar);
}

// -----------------------StopEvadingCharacter------------------
// Stops evading the character...duh. 
void Character::StopEvadingCharacter()
{
	m_pSteeringBehavior->EvadeOff();
}


// ==================================== Group Stuff =================================

// ------------------FollowCharacter--------------------
// Modifies the steering behavior so that the character
// follows the specified leader at the given offset

void Character::FollowCharacter(Character* aChar, Vector2D offset)
{
	m_pSteeringBehavior->OffsetPursuitOn(aChar, offset);
	return;
}

// --------------StopFollowingCharacter--------------
// Simply turns off offset pursuit in it's instance
// of a steeering behavior.

void Character::StopFollowingCharacter()
{
	m_pSteeringBehavior->OffsetPursuitOff();
	return;
}

//------------------GroupID--------------------
// Retrieve the ID of the group to which this
// character belongs to.

int Character::GroupID()
{
	return m_iGroupID;
}

//-------------------AddToGroup----------------
// Merely changes the GroupID to the given ID

void Character::AddToGroup(int id)
{
	m_iGroupID = id;
	return;
}

//------------RemoveFromGroup---------------
// Removes the character from it's current 
// group.
void Character::RemoveFromGroup()
{
	m_iGroupID = -1;
}



// ======================================== Can't think of a title ====================================

// Sets the arrive destination and turns on arrive
void Character::SetArriveDestination(Vector2D dest)
{
	m_pSteeringBehavior->SetTarget(dest);
	m_pSteeringBehavior->ArriveOn();
	return;
}

void Character::StopArriving()
{
	m_pSteeringBehavior->ArriveOff();
	return;
}


void Character::ForgetTheFreakinPath()
{
	m_pSteeringBehavior->FollowPathOff();
	return;
}

void Character::TakePathToLocation(Path* myPath)
{
	m_pSteeringBehavior->SetPath(myPath);
	m_pSteeringBehavior->FollowPathOn();
	return;
}

void Character::TurnSeparationOn(list<Character*> neighbors)
{
	m_pSteeringBehavior->SeparationOn(neighbors);
	return;
}

void Character::TurnSeparationOff()
{
	m_pSteeringBehavior->SeparationOff();
	return;
}